package com.zx.scp.service.impl;

import com.aipg.common.AipgReq;
import com.aipg.common.AipgRsp;
import com.aipg.common.InfoReq;
import com.aipg.common.XSUtil;
import com.aipg.payreq.Body;
import com.aipg.payreq.Trans_Detail;
import com.aipg.payreq.Trans_Sum;
import com.aipg.payresp.Ret_Detail;
import com.aipg.rtreq.Trans;
import com.aipg.rtrsp.TransRet;
import com.aipg.transquery.TransQueryReq;
import com.allinpay.XmlTools;
import com.zx.map.service.model.*;
import com.zx.scp.service.OutcomeService;
import com.zx.scp.utils.PropertyUtil;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by IntelliJ IDEA. User: Administrator Date: 15-5-31 Time: 上午10:26 To
 * change this template use File | Settings | File Templates.
 */
@Component("outcomeService")
public class OutcomeServiceImpl implements OutcomeService {
    /**
     * log
     */
    private static Logger log = Logger.getLogger(OutcomeServiceImpl.class);


    private static final String DEFAULTLEVEL = "5";// 处理级别 N(1) 0-9 0优先级最低，默认为5
    private static final String DEFAULTDATATYPE = "2";// 数据格式 N(1) 2：xml格式
    private static final String DEFAULTVERSION = "03";// 版本 C(2) 03
    private static final String BUSSINESSCODE = "09900";

    /**
     * 组装报文头部
     *
     * @param trxcod
     * @return 100001 收款 100002 付款 100014 实时代付 100011 实时收款 100005 自动扣商户银行款充值
     * 100006 从商户银行户扣取商户手续费 100031 代收结算款 100041 代付失败退款 100042 代付退票退款
     * 100043 代付多余退款 100100 商户汇款充值 100101 商户款划出 100105 银行退票 日期：Sep 9,
     * 2012
     */
    private InfoReq makeReq(String trxcod) {

        InfoReq info = new InfoReq();
        info.setTRX_CODE(trxcod);// 交易代码 C(1, 20) 100001
        // 交易批次号 必须全局唯一，商户提交的批次号必须以商户号开头以保证与其他商户不冲突，
        // 一旦冲突交易将无法提交；建议格式：商户号+时间+固定位数顺序流水号。
        // 该字段值用于后续的查询交易、对账文件等的唯一标识，对应通联系统中的交易文件名，
        // 可以在通联系统交易查询页面查询到该值
        StringBuffer str = new StringBuffer();
        str.append(PropertyUtil.getValue("merchantId")).append(
                String.valueOf(System.currentTimeMillis()));
        info.setREQ_SN(str.toString());
        info.setUSER_NAME(PropertyUtil.getValue("userName"));// 用户名
        info.setUSER_PASS(PropertyUtil.getValue("password"));// 用户密码
        info.setMERCHANT_ID(PropertyUtil.getValue("merchantId"));// 商户代码
        info.setLEVEL(DEFAULTLEVEL);// 处理级别 N(1) 0-9 0优先级最低，默认为5
        info.setDATA_TYPE(DEFAULTDATATYPE);// 数据格式 N(1) 2：xml格式
        info.setVERSION(DEFAULTVERSION);// 版本 C(2) 03
        return info;
    }

    /**
     * 日期：Sep 4, 2012 功能：实时单笔代收付， 100011是实时代笔代收，100014是实时单笔代付
     *
     * @throws Exception
     */

    public OutcomeResponse singleOutcome(OutcomeModel outcomeModel,
                                         boolean isTLTFront) throws Exception {
        log.info("单笔结算请求：" + ToStringBuilder.reflectionToString(outcomeModel));
        OutcomeResponse outcomeResponse = new OutcomeResponse();
        try {
            // 1、根据交易上送信息 构造xml 调用第三方单笔代付通道出款
            String singleOutcomeUrl = PropertyUtil.getValue("outcomeUrl");
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            AipgReq aipgReq = new AipgReq();
            Trans trans = new Trans();
            trans.setBUSINESS_CODE(BUSSINESSCODE);// 汇款转账类
            trans.setMERCHANT_ID(PropertyUtil.getValue("merchantId"));
            trans.setSUBMIT_TIME(df.format(new Date()));
            trans.setACCOUNT_NAME(outcomeModel.getAccountName());// 中文转码
            trans.setACCOUNT_NO(outcomeModel.getAccountNo());
            trans.setACCOUNT_PROP(null != outcomeModel.getAccountProp() ? outcomeModel
                    .getAccountProp() : "0");
            trans.setACCOUNT_TYPE(null != outcomeModel.getAccountType() ? outcomeModel
                    .getAccountType() : "00");
            trans.setAMOUNT(outcomeModel.getAmount());
            trans.setBANK_CODE(outcomeModel.getBankCode());
            trans.setCURRENCY(null != outcomeModel.getCurrency() ? outcomeModel
                    .getCurrency() : "CNY");
            trans.setTEL(outcomeModel.getTelephone());
            InfoReq info = this.makeReq("100014");// 100014是实时单笔代付
            aipgReq.setINFO(info);
            aipgReq.addTrx(trans);
            String xml = XmlTools.buildXml(aipgReq, true);
            String returnXml = sendToTlt(xml, isTLTFront, singleOutcomeUrl);
            AipgRsp response = XSUtil.parseRsp(returnXml);
            log.info("单笔结算请求返回：" + ToStringBuilder.reflectionToString(response));
            // 2、构造返回信息 单笔返回状态即终态
            boolean isTradeFail = this.isTradeFail(response.getINFO().getRET_CODE());
            if (isTradeFail) {
                outcomeResponse.setSettleOrdId(info.getREQ_SN());
                outcomeResponse.setReturnCode("9900");
                outcomeResponse.setErrMsg("通联系统处理异常");
                log.info("通联系统处理异常");
            } else {//通联明确失败的判断为失败 其他均为成功
                outcomeResponse.setSettleOrdId(info.getREQ_SN());
                //先将此类情况均设置处理成功  如果有具体的处理信息  再以处理信息为准
                outcomeResponse.setReturnCode("0000");
                outcomeResponse.setErrMsg(response.getINFO().getERR_MSG());
                log.info("通联明确失败的判断为失败 其他均为成功");
                List<TransRet> returnInfo = response.getTrxData();
                if (null != returnInfo && returnInfo.size() == 1) {
                    TransRet oo = returnInfo.get(0);
                    outcomeResponse.setSettleOrdId(info.getREQ_SN());
                    outcomeResponse.setErrMsg(oo.getERR_MSG());
                    //retcode1 为0000，retcode2 为4000 表示付款跨行交易已发到银行处理，如果交易没 有退票则黙认成功
                    if (isDefaultSuccess(oo.getRET_CODE())) {
                        outcomeResponse.setReturnCode("0000");
                    } else {
                        outcomeResponse.setReturnCode(oo.getRET_CODE());
                    }
                    outcomeResponse.setSettleDay(oo.getSETTLE_DAY());
                    log.info("单笔结算请求处理成功");
                }
            }
            return outcomeResponse;
        } catch (Exception e) {
            log.info("系统内部异常", e);
            outcomeResponse.setReturnCode("9999");
            outcomeResponse.setErrMsg("系统内部异常");
            return outcomeResponse;
        }
    }

    /**
     * 中间状态和固定成功的 默认成功 
     * @param retCode
     * @return
     */
    private boolean isDefaultSuccess(String retCode) {
    	//2000、2001、2003、2005、2007、2008、0003、0014
    	boolean dealFlag = false;
        if ("0000".equals(retCode) || "4000".equals(retCode) || "2000".equals(retCode) || "2001".equals(retCode) || "2003".equals(retCode)
                || "2005".equals(retCode) || "2007".equals(retCode) || "2008".equals(retCode) || "0003".equals(retCode)|| "0014".equals(retCode)) {
            dealFlag = true;
        }
        return dealFlag;
	}

	/**
     * 是否处理失败
     *
     * @param retCode
     * @return
     */
    private boolean isTradeFail(String retCode) {
        //1000 报文内容检查错或者处理错（具体内容见返回错误信息）
        //1001 报文解释错
        //1002 冲正时无此交易
        //1999 本批交易已经全部失败(最终结果)
        //0001 系统处理失败表示最终失败
        //0002 已撤销表示最终失败
        //1000   报文内容检查错或者处理错（具体内容见返回错误信息） 表示查询交			易本身处理失败，需重查
        //2002 商户审核不通过 最终失败
        //2004 不通过受理最终失败
        //2006不通过复核最终失败
        boolean dealFlag = false;
        if ("1000".equals(retCode) || "1001".equals(retCode) || "1002".equals(retCode) || "1999".equals(retCode) || "0001".equals(retCode)
                || "0002".equals(retCode) || "2002".equals(retCode) || "2004".equals(retCode) || "2006".equals(retCode)) {
            dealFlag = true;
        }
        return dealFlag;
    }

    /**
     * 日期：Sep 4, 2012 功能：批量结算 100002
     *
     * @throws Exception
     */
    public BatchOutcomeResponse batchOutCome(
            BatchOutcomeModel batchOutcomeModel, boolean isTLTFront)
            throws Exception {
        log.info("批量结算请求："
                + ToStringBuilder.reflectionToString(batchOutcomeModel));
        BatchOutcomeResponse bor = new BatchOutcomeResponse();
        try {
            String batchOutcomeUrl = PropertyUtil.getValue("outcomeUrl");
            // 构造报文体内容 ：汇总记录 +明细数据
            Body body = new Body();
            List<Trans_Detail> transList = new ArrayList<Trans_Detail>();
            // Integer countNum = 0;// 批量操作计数器
            BigDecimal bdSum = new BigDecimal("0");// 金额累计
            for (OutcomeModel oo : batchOutcomeModel.getOutcomeModelList()) {
                Trans_Detail trans_detail = new Trans_Detail();
                trans_detail.setSN(oo.getBatchSn());
                trans_detail.setACCOUNT_NAME(oo.getAccountName());
                trans_detail.setACCOUNT_PROP(oo.getAccountProp());
                trans_detail.setACCOUNT_NO(oo.getAccountNo());
                trans_detail.setBANK_CODE(oo.getBankCode());
                trans_detail.setPROVINCE(oo.getProvince());
                trans_detail.setCITY(oo.getCity());
                trans_detail.setBANK_NAME(oo.getBranchName());
                trans_detail.setAMOUNT(oo.getAmount());
                trans_detail.setCURRENCY(oo.getCurrency());
                transList.add(trans_detail);
                bdSum = bdSum.add(new BigDecimal(oo.getAmount()));
            }
            Trans_Sum transSum = new Trans_Sum();
            transSum.setBUSINESS_CODE(BUSSINESSCODE);
            transSum.setMERCHANT_ID(PropertyUtil.getValue("merchantId"));
            transSum.setTOTAL_ITEM(Integer.toString((batchOutcomeModel
                    .getOutcomeModelList().size())));
            transSum.setTOTAL_SUM(bdSum.toString());
            body.setTRANS_SUM(transSum);
            body.setDetails(transList);
            // 构造申请信息 指定交易类型
            AipgReq aipgReq = new AipgReq();
            InfoReq info = this.makeReq("100002");// 批量代付
            aipgReq.setINFO(info);
            aipgReq.addTrx(body);
            String xml = XmlTools.buildXml(aipgReq, true);
            String returnXml = sendToTlt(xml, isTLTFront, batchOutcomeUrl);
            AipgRsp response = XSUtil.parseRsp(returnXml);
            if ("0000".equals(response.getINFO().getRET_CODE())) {
                com.aipg.payresp.Body oo = (com.aipg.payresp.Body) response.getTrxData().get(0);
                List<Ret_Detail> retDetails = oo.getDetails();
                List<OutcomeResponse> outResponse = new ArrayList<OutcomeResponse>();
                for (Ret_Detail rd : retDetails) {
                    OutcomeResponse or = new OutcomeResponse();
                    or.setBatchSn(rd.getSN());
                    or.setReturnCode(rd.getRET_CODE());
                    or.setErrMsg(rd.getERR_MSG());
                    or.setSettleOrdId(info.getREQ_SN());
                    outResponse.add(or);
                }
                bor.setList(outResponse);
                bor.setReturnCode("0000");
                bor.setSettleOrdId(info.getREQ_SN());
            } else {
                log.info("系统处理异常");
                bor.setReturnCode("9000");
                bor.setErrMsg("系统处理异常");
            }
            return bor;
        } catch (Exception e) {
            log.info("系统内部异常", e);
            bor.setReturnCode("9999");
            bor.setErrMsg("系统内部异常");
            return bor;
        }
    }

    /**
     * @param reqsn      交易流水号
     * @param isTLTFront 是否通过前置机 日期：Sep 4, 2012 功能：交易结果查询
     * @throws Exception
     */

    public void queryTradeInfo(String reqsn, boolean isTLTFront)
            throws Exception {
        String queryTradeUrl = PropertyUtil.getValue("queryTradeUrl");
        AipgReq aipgReq = new AipgReq();
        InfoReq info = this.makeReq("200004");// 交易结果查询
        aipgReq.setINFO(info);
        TransQueryReq dr = new TransQueryReq();
        dr.setMERCHANT_ID(PropertyUtil.getValue("merchantId"));
        dr.setQUERY_SN(reqsn);// 原请求交易中的REQ_SN的值
        dr.setSTATUS(2);// 交易状态条件, 0成功,1失败, 2全部,3退票
        dr.setTYPE(1);// 0.按完成日期1.按提交日期，默认为1
        aipgReq.addTrx(dr);
        String xml = XmlTools.buildXml(aipgReq, true);
        sendToTlt(xml, isTLTFront, queryTradeUrl);
    }




    /**
     * 报文发送接收处理
     *
     * @param xml
     * @param url
     * @param isFront
     * @return
     * @throws java.io.UnsupportedEncodingException
     * @throws Exception
     */
    private String sendXml(String xml, String url, boolean isFront)
            throws UnsupportedEncodingException, Exception {
        log.info("======================发送报文======================：\n" + xml);
        System.out.println("======================发送报文======================：\n" + xml);
        String resp = XmlTools.send(url, xml);
        log.info("======================响应内容======================: \n" + resp);
        System.out.println("======================响应内容======================: \n" + resp);
        boolean flag = this.verifyMsg(resp,
                PropertyUtil.getValue("tltcerPath"), isFront);
        if (flag) {
            log.info("响应内容验证通过");
            return resp;
        } else {
            log.info("响应内容验证不通过");
            return null;
        }
    }

    /**
     * @param xml
     * @param flag
     * @param url
     * @return
     */
    private String sendToTlt(String xml, boolean flag, String url) {
        try {
            if (!flag) {
                xml = this.signMsg(xml);
            } else {
                xml = xml.replaceAll("<SIGNED_MSG></SIGNED_MSG>", "");
            }
            return sendXml(xml, url, flag);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 报文签名
     *
     * @return 日期：Sep 9, 2012
     * @throws Exception
     */
    private String signMsg(String xml) throws Exception {
        xml = XmlTools.signMsg(xml, PropertyUtil.getValue("pfxPath"),
                PropertyUtil.getValue("pfxPassword"), false);
        return xml;
    }

    /**
     * 验证签名
     *
     * @param msg
     * @return 日期：Sep 9, 2012
     * @throws Exception
     */
    private boolean verifyMsg(String msg, String cer, boolean isFront)
            throws Exception {
        boolean flag = XmlTools.verifySign(msg, cer, false, isFront);
        log.info("验签结果[" + flag + "]");
        return flag;
    }
}
